package com.bc.service;


import java.io.IOException;
import java.io.InterruptedIOException;
import java.lang.reflect.ParameterizedType;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpResponseException;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import com.bc.utils.network.GsonUtil;
import com.bc.utils.network.HttpClient;
import com.google.gson.JsonParseException;

/**
 * 
 * 2013-8-9 上午11:29:10
 * <p>
 * 类描述：所有网络服务的基类，子类在继承此基类时需要传递两个泛型参数，分别用来表示请求和响应。
 * 
 */
public abstract class GenericService<REQUEST extends BaseRequest, RESPONSE extends BaseResponse> {

	private static final String TAG = "网络访问";

	/** 这次请求是一次普通的加载请求 */
	protected static final int REQUEST_CODE_LOAD = -1;

	/** 这次请求要加载更多 */
	protected static final int REQUEST_CODE_MORE = -2;

	protected Context mContext;

	private REQUEST mRequest;// 对子类不可见
	protected RESPONSE mResponse;// 对子类可见

	protected Class<RESPONSE> mResponseType;

	private NetworkTask mTask;

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public GenericService(Context context) {
		mContext = context;
		ParameterizedType pType = (ParameterizedType) getClass().getGenericSuperclass();
		mResponseType = (Class) pType.getActualTypeArguments()[1];
	}

	/**
	 * 发起网络请求，你需要在调用此方法之前，调用{@link #setRequest(BaseRequest)}来设置请求对象
	 * 注意，同一个service，如果前一个网络请求还没完成，会取消前一个请求。
	 * 
	 * @param requestCode
	 *            请求码，用于区分不同的请求类型。参看{@link #REQUEST_CODE_LOAD},
	 *            {@link #REQUEST_CODE_MORE}
	 * @param listener
	 *            用于回调的监听
	 */
	@SuppressLint("NewApi")
	protected void request(int requestCode, RequestListener listener) {

		if (mTask != null) {// 上次请求还没处理完
			mTask.cancel(true);
			mResponse = null;
			Log.w(TAG, mRequest.getClass().getSimpleName() + " 前一个任务被取消");
		}

		mTask = new NetworkTask(requestCode, listener);
		if (android.os.Build.VERSION.SDK_INT >= 9) {
			mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
		} else {
			mTask.execute("");
		}

	}

	/** 取消服务，在Activity的onDestroy或Fragment的onDestroyView中调用 */
	public void cancel() {
		if (mTask != null) {
			mTask.cancel(true);
		}
	}

	/**
	 * 为即将发起的网络请求设置请求对象，参看{@link #request(int, RequestListener)},
	 * {@link #onRequest(int, BaseRequest)}
	 * 
	 * @param request
	 */
	protected void setRequest(REQUEST request) {
		mRequest = request;
	}

	/**
	 * 这个方法在异步线程中执行，你可以在这个方法中执行那些耗时操作，例如查询数据库，把必要的值如版本号设置给请求对象
	 * 
	 * @param requestCode
	 *            请求码，用于区分不同的请求类型。参看{@link #REQUEST_CODE_LOAD},
	 *            {@link #REQUEST_CODE_MORE}
	 * @param request
	 *            请求对象，这是你通过{@link #setRequest(BaseRequest)}传递的对象
	 */
	protected abstract void onRequest(int requestCode, REQUEST request);

	/**
	 * 这个方法在异步线程中执行。你可能需要对比版本号，以决定是从数据库中读取数据，还是将数据缓存到数据库当中。
	 * 
	 * @param requestCode
	 *            请求码，用于区分不同的请求类型。
	 * @param request
	 *            这就是你通过 {@link #setRequest(BaseRequest)}设置的请求对象
	 * @param response
	 *            与request对应的响应对象，这里面有你想要的数据
	 * @throws IOException
	 *             子类请不要捕获此异常
	 */
	protected abstract void onResponse(int requestCode, REQUEST request, RESPONSE response) throws IOException;

	private class NetworkTask extends AsyncTask<String, Void, CWException> {
		private int mRequestCode;
		private RequestListener mListener;

		NetworkTask(int requestCode, RequestListener listener) {
			mRequestCode = requestCode;
			mListener = listener;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			mListener.onPrepare();
		}

		@Override
		protected CWException doInBackground(String... params) {

			REQUEST request = mRequest;

			try {

				try {

					onRequest(mRequestCode, request);
					String url = request.getUrl();
					Log.i(TAG, request.getClass().getSimpleName() + " 请求路径：" + url);

					if (!NetWorkHelper.checkNetState(mContext)) {// 这两句必须在onRequest之后，否则缓存机制不会正常运行。
						throw new CWException(CWException.EX_NETWORK_DISABLE, "网络不可用。");
					}

					String content = doNetworkTask(request, url);
					Log.i(TAG, request.getClass().getSimpleName() + " 响应内容：" + content);

					if (!this.isCancelled()) {

						mResponse = GsonUtil.getGsonInstance().fromJson(content, mResponseType);

						int error = Integer.valueOf(mResponse.Error);

						if (Integer.valueOf(mResponse.Error) != 0) {

							CWException ex = null;

							switch (error) {

							case BaseResponse.ERROR_PARAM:
								ex = new CWException(CWException.EX_CLIENT_ERROR, "客户端请求参数错误。");
								break;
							case BaseResponse.ERROR_SECRET:
								ex = new CWException(CWException.EX_CLIENT_ERROR, "加密参数错误。");
								break;
							default:
							case BaseResponse.ERROR_SQL:
								ex = new CWException(CWException.EX_SERVER_ERROR, "服务器数据查询错误。");
								break;
							}
							throw ex;
						}
						handleResponse(request, content);
						onResponse(mRequestCode, request, mResponse);
					}
				} catch (JsonParseException e) {
					e.printStackTrace();
					throw new CWException(CWException.EX_CLIENT_ERROR, "json解析错误。");
				} catch (InterruptedIOException e) {
					e.printStackTrace();
					throw new CWException(CWException.EX_TIMEOUT, "网络太烂，访问超时。");
				} catch (HttpResponseException e) {
					e.printStackTrace();
					throw new CWException(CWException.EX_SERVER_ERROR, "服务器端发生错误:" + e.getStatusCode());
				}

			} catch (CWException e) {
				if (request != null)
					Log.e(TAG, request.getClass().getSimpleName() + " CWException：" + e.getMessage());
				return interceptException(e, mRequestCode, request, mListener);
			} catch (Exception e) {
				e.printStackTrace();
				return interceptException(new CWException(CWException.EX_UNKNOWN, "未知的异常，必要时请对此异常归类。"), mRequestCode,
						request, mListener);
			} finally {
				mTask = null;
				if (request == mRequest) {
					mRequest = null;
				}
			}
			return null;
		}

		@Override
		protected void onPostExecute(CWException ex) {
			if (!isCancelled()) {
				handleException(ex, mListener);
			}
		}
	}

	protected String doNetworkTask(REQUEST request, String url) throws IOException, ClientProtocolException,
			InterruptedIOException {
		String content = "";

		for (int i = 0; i < 3; i++) {// 访问超时重试3次
			try {
				Log.i(TAG, request.getClass().getSimpleName() + " 第" + (i + 1) + "次请求");
				if (NetWorkHelper.isCMWAP(mContext)) {
					content = HttpClient.getViaCmwap(url);
				} else {
					content = HttpClient.get(url);
				}
				break;
			} catch (InterruptedIOException e) {
				if (i == 2) {
					throw e;
				} else {
					continue;
				}
			}
		}
		return content;
	}

	protected void handleException(CWException ex, RequestListener listener) {
		if (ex == null) {
			listener.onComplete(0);
		} else {
			listener.onComplete(ex.getErrorCode());
		}
	}

	protected void handleResponse(REQUEST request, String content) {
		// for subclass

	}

	protected CWException interceptException(CWException ex, int requestCode, REQUEST request, RequestListener listener) {
		mResponse = null;// 置空，避免使用上次的响应
		return ex;
	}

	public boolean isRunning() {
		return mTask != null ? mTask.getStatus() == AsyncTask.Status.RUNNING : false;
	}
}
